package com.ruoyi.common.utils;

import cn.hutool.json.JSONObject;
import com.ruoyi.common.exception.ServiceException;

import javax.crypto.Cipher;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * php api签名工具
 */
public class PhpApiSignUtils {

    /**
     * 定义加密方式
     */
    private static final String KEY_RSA = "RSA";
    /**
     * 定义签名算法
     */
    private final static String KEY_RSA_SIGNATURE = "MD5withRSA";

    /**
     * 使用 MD5withRSA 对 request.body 进行签名，并进行 Base64 编码
     *
     * @param requestBody 请求体内容（json格式 示例：{"requestId": "1001"}）
     * @param privateKey  私钥字符串（Base64 编码）
     * @return 签名后的 Base64 字符串
     * @throws Exception 异常信息
     */
    public static String sign(String requestBody, String privateKey) {
        System.out.println("加签请求体 = " + requestBody);
        // 当请求体为空时，无需加签
        if (requestBody == null || requestBody.isEmpty()) {
            return null;
        }
        try {
            //将私钥加密数据字符串转换为字节数组
            byte[] data = requestBody.getBytes(StandardCharsets.UTF_8);
            // 获取私钥
            byte[] keyBytes = Base64.getDecoder().decode(privateKey);
            // 构造PKCS8EncodedKeySpec对象
            PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(keyBytes);
            // 指定的加密算法
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
            // 取私钥对象
            PrivateKey key = factory.generatePrivate(pkcs);
            // ⽤私钥对信息⽣成数字签名
            // 使用 RSA 对哈希值进行签名
            Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
            signature.initSign(key);
            signature.update(data);
            byte[] signedBytes = signature.sign();
            // 对签名结果进行 Base64 编码
            return Base64.getEncoder().encodeToString(signedBytes);
        } catch (Exception e) {
            throw new ServiceException("验签加密失败");
        }
    }

    public static boolean verify(String encryptedStr, String publicKey, String sign) {
         try {
            //将私钥加密数据字符串转换为字节数组
            byte[] data = encryptedStr.getBytes(StandardCharsets.UTF_8);
            // 解密由base64编码的公钥
            byte[] bytes = Base64.getDecoder().decode(publicKey);
            // 构造X509EncodedKeySpec对象
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
            // 指定的加密算法
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
            // 取公钥对象
            PublicKey key = factory.generatePublic(keySpec);
            // ⽤公钥验证数字签名
            Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
            signature.initVerify(key);
            signature.update(data);
            return signature.verify(Base64.getDecoder().decode(sign));
            } catch (Exception e) {
            throw new ServiceException("验签加密失败");
            }
        }

    public static boolean testVerify(String encryptedStr, String publicKey, String sign) {
        try {
            //将私钥加密数据字符串转换为字节数组
            byte[] data = encryptedStr.getBytes(StandardCharsets.UTF_8);
            // 解密由base64编码的公钥
            byte[] bytes = Base64.getDecoder().decode(publicKey);
            // 构造X509EncodedKeySpec对象
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
            // 指定的加密算法
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
            // 取公钥对象
            PublicKey key = factory.generatePublic(keySpec);
            // ⽤公钥验证数字签名
            Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
            signature.initVerify(key);
            signature.update(data);
            return signature.verify(Base64.getDecoder().decode(sign));
        } catch (Exception e) {
            throw new ServiceException("验签加密失败");
        }
    }

    /**
     * 将对象转换为JSON格式的字符串
     * @param obj
     * @return
     */
    public static String convertObjectToJson(Object obj) {
        // 创建一个Map来存储对象的属性和属性值
        Map<String, Object> map = new HashMap<>();
        // 获取对象的所有字段
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);  // 设置字段可访问
            try {
                Object value = field.get(obj);
                // 过滤掉null值
                if (value != null && !"".equals(value.toString())) {
                    map.put(field.getName(), value);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        // 使用JSONObject将Map转换为JSON字符串
        JSONObject jsonObject = new JSONObject(map);
        System.out.println("jsonObject.toString()" + jsonObject.toString());
        return jsonObject.toString();
    }

    /**
     * 将对象转换为查询字符串格式，并过滤掉 null 和空字符串
     * @param obj 对象
     * @return 查询字符串
     */
    public static String convertObjectToQueryString(Object obj) {
        Map<String, String> params = new HashMap<>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(obj);
                if (value != null && !value.toString().isEmpty()) {
                    params.put(field.getName(), value.toString());
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return generatorParamString(params, "UTF-8");
    }

    /**
     * 将parameters中数据转换成用"&"链接的http请求参数形式
     *
     * @param parameters
     * @return
     */
    private static String generatorParamString(Map<String, String> parameters, String encoding) {
        StringBuffer params = new StringBuffer();
        if (parameters != null) {
            for (Iterator<String> iter = parameters.keySet().iterator(); iter
                .hasNext(); ) {
                String name = iter.next();
                String value = parameters.get(name);
                params.append(name + "=");
                try {
                    params.append(URLEncoder.encode(value, encoding));
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e.getMessage(), e);
                } catch (Exception e) {
                    String message = String.format("'%s'='%s'", name, value);
                    throw new RuntimeException(message, e);
                }
                if (iter.hasNext()) {
                    params.append("&");
                }
            }
        }
        return params.toString();
    }

    public static void main(String[] args) {

        String requestBody = "{\"memberId\": \"2025021316452798\",\n" +
            "                                          \"matrixAccount\": \"\",\n" +
            "                                          \"cardId\": \"XR1913544596470632448\",\n" +
            "                                          \"cardStatus\":\"frozen\",\n" +
            "                                          \"cardNumber\":\"531993******4620\",\n" +
            "                                          \"cardholderId\":\"CH1910214084926640128\",\n" +
            "                                          \"produceStatus\":\"\",\n" +
            "                                          \"trackingNumber\":\"\",\n" +
            "                                          \"updatedAt\":\"2025-05-08T02:40:23\"}";
        String photonPayPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCj16fR4XG8Bi3+4VIRHWf+ctI+xndKEPVTohiPR+hiEtbud9kDE9ZDYggptZ3Y5KGHJI338Vakl7DQMCSwt/E7JSPwq7W68zrlKeEdFMd/IXwFVivuK1NVHwgOqwBL0JiK3NKQeB5DI0kLoN3NM2mHpH2e0uyo4PWBYeKBqYjayQIDAQAB";
        String signStr = "FUG2DcIapW/thDoBMgQPRMi8AjTeECO/3XGVGM3F3oXeHXoNwy6k7QTfm1JiAhK+enOeXQd75QvOK0apinKthKVxl7/T0rIlnbsk5uYFtsnwEO4O/ryc0j2VRNO9wd+DWogPB7lBhcvMMZqlchzOS44n79zES6r1vISKlzWOHcI=";
        boolean testVerify = testVerify(requestBody, photonPayPublicKey, signStr);
        System.out.println("testVerify = " + testVerify);
    }
}
